/******************************************************************
 * 个人博客：https://blog.csdn.net/2302_80277720
 * 嵌入式Linux学习交流群：1005210698
 * 欢迎各位大佬和萌新来加入交流学习
 * Change Logs:
 * Date           Author       Notes
 * 2025-08-19     喝呜昂黄    first version
 ******************************************************************/

#include <fcntl.h>
#include <stdbool.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>

#include "xf86drm.h"
#include "xf86drmMode.h"

struct drm_device {
    uint32_t width;                        // 显示器的宽的像素点数量
    uint32_t height;                       // 显示器的高的像素点数量
    uint32_t pitch;                        // 每行占据的字节数
    uint32_t handle;                       // drm_mode_create_dumb的返回句柄
    uint32_t size;                         // 显示器占据的总字节数
    uint32_t *vaddr;                       // mmap的首地址
    uint32_t fb_id;                        // 创建的framebuffer的id号
    struct drm_mode_create_dumb create;    // 创建的dumb
    struct drm_mode_map_dumb map;          // 内存映射结构体
};

drmModeConnectorPtr conn;    // connetor相关的结构体
drmModeResPtr res;           // 资源
uint32_t conn_id;            // connetor的id号
uint32_t crtc_id;            // crtc的id号
int fd;                      // 文件描述符

#define RED 0XFF0000
#define GREEN 0X00FF00
#define BLUE 0X0000FF

struct drm_device buf;

static char *get_sub_format(drmModeSubPixel sub_pixel) {
    switch (sub_pixel) {
        case DRM_MODE_SUBPIXEL_HORIZONTAL_RGB:
            return "horizontal RGB";
        case DRM_MODE_SUBPIXEL_HORIZONTAL_BGR:
            return "horizontal BGR";
        case DRM_MODE_SUBPIXEL_VERTICAL_RGB:
            return "vertical RGB";
        case DRM_MODE_SUBPIXEL_VERTICAL_BGR:
            return "vertical BGR";
        case DRM_MODE_SUBPIXEL_NONE:
            return "none";
        case DRM_MODE_SUBPIXEL_UNKNOWN:
        default:
            return "未知的";
    }
}

static char *get_connector_type(uint32_t type) {
    switch (type) {
        case DRM_MODE_CONNECTOR_Unknown:
            return "未知的";
        case DRM_MODE_CONNECTOR_VGA:
            return "VGA";
        case DRM_MODE_CONNECTOR_DVII:
            return "DVI-I（双链路数字/模拟）";
        case DRM_MODE_CONNECTOR_DVID:
            return "DVI-D（双链路数字）";
        case DRM_MODE_CONNECTOR_DVIA:
            return "DVI-A（模拟）";
        case DRM_MODE_CONNECTOR_Composite:
            return "复合视频（CVBS）";
        case DRM_MODE_CONNECTOR_SVIDEO:
            return "S-视频（Y/C分离）";
        case DRM_MODE_CONNECTOR_LVDS:
            return "LVDS（低压差分信号）";
        case DRM_MODE_CONNECTOR_Component:
            return "分量视频（YPbPr/RGB）";
        case DRM_MODE_CONNECTOR_9PinDIN:
            return "9针DIN（SCART/RS-422）";
        case DRM_MODE_CONNECTOR_DisplayPort:
            return "DisplayPort（DP）";
        case DRM_MODE_CONNECTOR_HDMIA:
            return "HDMI-A（标准HDMI）";
        case DRM_MODE_CONNECTOR_HDMIB:
            return "HDMI-B（双链路HDMI，罕见）";
        case DRM_MODE_CONNECTOR_TV:
            return "电视（射频/AV端子）";
        case DRM_MODE_CONNECTOR_eDP:
            return "eDP";
        case DRM_MODE_CONNECTOR_VIRTUAL:
            return "虚拟连接器（无物理接口）";
        case DRM_MODE_CONNECTOR_DSI:
            return "DSI";
        case DRM_MODE_CONNECTOR_DPI:
            return "DPI";
        case DRM_MODE_CONNECTOR_WRITEBACK:
            return "回写连接器（用于帧缓冲捕获）";
        default:
            return "未定义的连接器类型";
    }
}
int main(int argc, char **argv) {
    int i;

    // 打开drm设备，设备会随设备树的更改而改变
    // 多个设备时，请留一下每个屏幕设备对应的drm设备
    fd = drmOpen("rockchip", NULL);
    if (fd < 0) {
        printf("Failed to open DRM device\n");
        return -1;
    }
    printf("DRM设备打开成功：fd=%d\n", fd);

    // 获取drm的信息
    res     = drmModeGetResources(fd);
    crtc_id = res->crtcs[0];
    conn_id = res->connectors[0];

    // 打印所有资源信息
    printf("===== DRM设备资源详情 (drmModeRes) =====\n");
    printf("帧缓冲(FB)信息:\n");
    printf("  数量: %d\n", res->count_fbs);
    if (res->count_fbs > 0 && res->fbs) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_fbs; i++) {
            printf("%u ", res->fbs[i]);
        }
        printf("\n");
    }
    printf("\nCRTC(显示控制器)信息:\n");
    printf("  数量: %d\n", res->count_crtcs);
    if (res->count_crtcs > 0 && res->crtcs) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_crtcs; i++) {
            printf("%u ", res->crtcs[i]);
        }
        printf("\n");
    }
    printf("\n连接器(Connector)信息:\n");
    printf("  数量: %d\n", res->count_connectors);
    if (res->count_connectors > 0 && res->connectors) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_connectors; i++) {
            printf("%u ", res->connectors[i]);
        }
        printf("\n");
    }
    printf("\n编码器(Encoder)信息:\n");
    printf("  数量: %d\n", res->count_encoders);
    if (res->count_encoders > 0 && res->encoders) {
        printf("  ID列表: ");
        for (int i = 0; i < res->count_encoders; i++) {
            printf("%u ", res->encoders[i]);
        }
        printf("\n");
    }
    printf("\n支持的显示尺寸范围:\n");
    printf("  宽度: %u (最小) ~ %u (最大) 像素\n", res->min_width, res->max_width);
    printf("  高度: %u (最小) ~ %u (最大) 像素\n", res->min_height, res->max_height);
    printf("======================================\n\n");

    conn       = drmModeGetConnector(fd, conn_id);
    buf.width  = conn->modes[0].hdisplay;
    buf.height = conn->modes[0].vdisplay;

    printf("== 连接器资源详情 (drmModeConnector) ==\n");
    printf("连接器信息 (ID: %u)\n", conn->connector_id);
    printf("类型: %s (ID: %u)\n", get_connector_type(conn->connector_type), conn->connector_type_id);
    printf("连接状态: %s\n", conn->connection == DRM_MODE_CONNECTED ? "已连接" : "未连接");
    printf("显示器尺寸: %ux%u mm\n", conn->mmWidth, conn->mmHeight);
    printf("子像素排列: %s\n", get_sub_format(conn->subpixel));
    printf("支持的显示模式数量: %d\n", conn->count_modes);
    if (conn->count_modes > 0) {
        printf("  模式列表（分辨率@刷新率）:\n");
        for (int i = 0; i < conn->count_modes; i++)
            printf("  %d: %ux%u @ %dHz %s\n", i, conn->modes[i].hdisplay, conn->modes[i].vdisplay, conn->modes[i].vrefresh,
                (conn->modes[i].flags & DRM_MODE_TYPE_PREFERRED) ? "(首选)" : "");
    }
    printf("\n可关联的编码器数量: %d\n", conn->count_encoders);
    if (conn->count_encoders > 0) {
        printf("  编码器ID列表: ");
        for (int i = 0; i < conn->count_encoders; i++) {
            printf("%u ", conn->encoders[i]);
        }
        printf("\n");
    }
    printf("\n当前关联的编码器ID: %u\n", conn->encoder_id);
    printf("======================================\n\n");

    /* create a dumb-buffer, the pixel format is XRGB888 */
    buf.create.width  = buf.width;
    buf.create.height = buf.height;
    buf.create.bpp    = 32;

    /* handle, pitch, size will be returned */
    drmIoctl(fd, DRM_IOCTL_MODE_CREATE_DUMB, &buf.create);

    printf("== struct drm_mode_create_dumb 详情 ==\n");
    printf("输入参数:\n");
    printf("  width:  %u 像素\n", buf.create.width);
    printf("  height: %u 像素\n", buf.create.height);
    printf("  bpp:    %u 位/像素\n", buf.create.bpp);
    printf("  flags:  %u\n", buf.create.flags);
    printf("输出参数:\n");
    printf("  handle: %u\n", buf.create.handle);
    printf("  pitch:  %u 字节\n", buf.create.pitch);
    printf("  size:   %llu 字节\n", (unsigned long long)buf.create.size);
    printf("========================================\n");

    /* bind the dumb-buffer to an FB object */
    buf.pitch  = buf.create.pitch;
    buf.size   = buf.create.size;
    buf.handle = buf.create.handle;
    drmModeAddFB(fd, buf.width, buf.height, 24, buf.create.bpp, buf.pitch, buf.handle, &buf.fb_id);

    /* map the dumb-buffer to userspace */
    buf.map.handle = buf.create.handle;
    drmIoctl(fd, DRM_IOCTL_MODE_MAP_DUMB, &buf.map);

    // 打印 struct drm_mode_map_dumb 信息
    printf("== struct drm_mode_map_dumb 详情 ==\n");
    printf("输入参数:\n");
    printf("  handle: %u\n", buf.map.handle);
    printf("  填充位: %u\n", buf.map.pad);
    printf("输出参数:\n");
    printf("  offset: 0x%llx\n", (unsigned long long)buf.map.offset);
    printf("====================================\n");

    buf.vaddr = mmap(0, buf.create.size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, buf.map.offset);

    /* initialize the dumb-buffer with white-color */
    memset(buf.vaddr, 0xFF, buf.size);

    // 设置CRTCS
    drmModeSetCrtc(fd, crtc_id, buf.fb_id, 0, 0, &conn_id, 1, &conn->modes[0]);

    sleep(2);
    // 清屏设置颜色
    for (i = 0; i < buf.width * buf.height; i++) buf.vaddr[i] = BLUE;

    sleep(3);

    for (i = 0; i < buf.width * buf.height; i++) buf.vaddr[i] = RED;

    sleep(3);

    struct drm_mode_destroy_dumb destroy = {};

    drmModeRmFB(fd, buf.fb_id);
    munmap(buf.vaddr, buf.size);
    destroy.handle = buf.handle;
    drmIoctl(fd, DRM_IOCTL_MODE_DESTROY_DUMB, &destroy);

    drmModeFreeConnector(conn);
    drmModeFreeResources(res);
    close(fd);

    return 0;
}